﻿using System;
using System.Runtime.InteropServices;

namespace RuoTeng.Win32
{
    using System.Drawing;
    using System.Drawing.Imaging;

    class Image : RuoTeng.Win32.Message
    {
        /// <summary>
        ///【Win32 API】 该函数通过使用指定的名字为一个设备创建设备上下文环境
        /// </summary>
        /// <param name="lpszDriver">指向一个以Null结尾的字符串的指针，该字符串为显示驱动指定DISPLAY或者指定一个打印驱动程序名，通常为WINSPOOL</param>
        /// <param name="lpszDevice">：指向一个以null结尾的字符串的指针，该字符串指定了正在使用的特定输出设备的名字，它不是打印机模式名。LpszDevice参数必须被使用</param>
        /// <param name="lpszOutput">该参数在32位应用中被忽略；并置为Null，它主要是为了提供与16位应用程序兼容</param>
        /// <param name="lpInitData">指向包含设备驱动程序的设备指定初始化数据的DEVMODE结构的指针，DocumentProperties函数检索指定设备获取已填充的结构，如果设备驱动程序使用用户指定的缺省初始化值。则lplnitData参数必须为Null</param>
        /// <returns></returns>
        [DllImport("gdi32.dll")]
        public static extern IntPtr CreateDC(string lpszDriver, string lpszDevice, string lpszOutput, IntPtr lpInitData);

        /// <summary>
        ///【Win32 API】 该函数删除指定的设备上下文环境(DC)
        /// </summary>
        /// <param name="hdc">设备上下文环境的句柄</param>
        /// <returns>成功，返回非零值；失败，返回零</returns>
        [DllImport("gdi32.dll")]
        public static extern bool DeleteDC(IntPtr hdc);

        /// <summary>
        ///【Win32 API】  该函数对指定的源设备环境区域中的像素进行位块（bit_block）转换，以传送到目标设备环境。
        /// </summary>
        /// <param name="hdcDest">指向目标设备环境的句柄</param>
        /// <param name="nXDest">指定目标矩形区域左上角的X轴逻辑坐标</param>
        /// <param name="nYDest">指定目标矩形区域左上角的Y轴逻辑坐标</param>
        /// <param name="nWidth">指定源和目标矩形区域的逻辑宽度</param>
        /// <param name="nHeight">指定源和目标矩形区域的逻辑高度</param>
        /// <param name="hdcSrc">指向源设备环境的句柄</param>
        /// <param name="nXSrc">指定源矩形区域左上角的X轴逻辑坐标</param>
        /// <param name="nYSrc">指定源矩形区域左上角的Y轴逻辑坐标</param>
        /// <param name="dwRop">
        ///          指定光栅操作代码。这些代码将定义源矩形区域的颜色数据，如何与目标矩形区域的颜色数据组合以完成最后的颜色
        ///          BLACKNESS：表示使用与物理调色板的索引0相关的色彩来填充目标矩形区域，（对缺省的物理调色板而言，该颜色为黑色）。 　
        ///          DSTINVERT：表示使目标矩形区域颜色取反。 　
        ///          MERGECOPY：表示使用布尔型的AND（与）操作符将源矩形区域的颜色与特定模式组合一起。 
        ///          MERGEPAINT：通过使用布尔型的OR（或）操作符将反向的源矩形区域的颜色与目标矩形区域的颜色合并。 
        ///          NOTSRCCOPY：将源矩形区域颜色取反，于拷贝到目标矩形区域。 　
        ///          NOTSRCERASE：使用布尔类型的OR（或）操作符组合源和目标矩形区域的颜色值，然后将合成的颜色取反。 
        ///          PATCOPY：将特定的模式拷贝到目标位图上。 　
        ///          PATPAINT：通过使用布尔OR（或）操作符将源矩形区域取反后的颜色值与特定模式的颜色合并。然后使用OR（或）操作符将该操作的结果与目标矩形区域内的颜色合并。 　
        ///          PATINVERT：通过使用XOR（异或）操作符将源和目标矩形区域内的颜色合并。 　
        ///          SRCAND：通过使用AND（与）操作符来将源和目标矩形区域内的颜色合并。 
        ///          SRCCOPY：将源矩形区域直接拷贝到目标矩形区域。 
        ///          SRCERASE：通过使用AND（与）操作符将目标矩形区域颜色取反后与源矩形区域的颜色值合并。 
        ///          SRCINVERT：通过使用布尔型的XOR（异或）操作符将源和目标矩形区域的颜色合并。 
        ///          SRCPAINT：通过使用布尔型的OR（或）操作符将源和目标矩形区域的颜色合并。 　
        ///          WHITENESS：使用与物理调色板中索引1有关的颜色填充目标矩形区域。（对于缺省物理调色板来说，这个颜色就是白色）。
        /// 
        /// </param>
        /// <returns></returns>
        [DllImport("gdi32.dll")]
        public static extern int BitBlt(IntPtr hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, UInt32 dwRop);

        /// <summary>
        ///【Win32 API】  该函数创建一个与指定设备兼容的内存设备上下文环境（DC）。通过GetDc()获取的HDC直接与相关设备沟通，而本函数创建的DC，则是与内存中的一个表面相关联
        /// </summary>
        /// <param name="hdc">有设备上下文环境的句柄，如果该句柄为NULL，该函数创建一个与应用程序的当前显示器兼容的内存设备上下文环境</param>
        /// <returns>如果成功，则返回内存设备上下文环境的句柄；如果失败，则返回值为NULL</returns>
        [DllImport("gdi32.dll")]
        public static extern IntPtr CreateCompatibleDC(IntPtr hdc);


        /// <summary>
        ///【Win32 API】  该函数创建与指定的设备环境相关的设备兼容的位图
        /// </summary>
        /// <param name="hdc">设备环境句柄</param>
        /// <param name="nWidth">指定位图的宽度，单位为像素</param>
        /// <param name="nHeight">指定位图的高度，单位为像素</param>
        /// <returns>如果函数执行成功，那么返回值是位图的句柄；如果函数执行失败，那么返回值为NULL</returns>
        [DllImport("gdi32.dll")]
        public static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int nWidth, int nHeight);

        /// <summary>
        /// 【Win32 API】 该函数选择一对象到指定的设备上下文环境中，该新对象替换先前的相同类型的对象
        /// </summary>
        /// <param name="hdc">设备上下文环境的句柄</param>
        /// <param name="hgdiobj">
        ///        被选择的对象的句柄，该指定对象必须由如下的函数创建
        ///        位图：CreateBitmap, CreateBitmapIndirect, CreateCompatible Bitmap, CreateDIBitmap, CreateDIBsection（只有内存设备上下文环境可选择位图，并且在同一时刻只能一个设备上下文环境选择位图）。 
        ///        画刷：CreateBrushIndirect, CreateDIBPatternBrush, CreateDIBPatternBrushPt, CreateHatchBrush, CreatePatternBrush, CreateSolidBrush。 
        ///        字体：CreateFont, CreateFontIndirect。 　
        ///        笔：CreatePen, CreatePenIndirect。 
        ///        区域：CombineRgn, CreateEllipticRgn, CreateEllipticRgnIndirect, CreatePolygonRgn, CreateRectRgn, CreateRectRgnIndirect。
        /// </param>
        /// <returns>如果选择对象不是区域并且函数执行成功，那么返回值是被取代的对象的句柄；如果选择对象是区域并且函数执行成功</returns>
        [DllImport("gdi32.dll")]
        public static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);


        /// <summary>
        ///【Win32 API】  该函数删除一个逻辑笔、画笔、字体、位图、区域或者调色板，释放所有与该对象有关的系统资源，在对象被删除之后，指定的句柄也就失效了
        /// </summary>
        /// <param name="hObject">逻辑笔、画笔、字体、位图、区域或者调色板的句柄</param>
        /// <returns>功，返回非零值；如果指定的句柄无效或者它已被选入设备上下文环境，则返回值为零</returns>
        [DllImport("gdi32.dll")]
        public static extern bool DeleteObject(IntPtr hObject);

        /// <summary>
        ///【Win32 API】  该函数检索一指定窗口的客户区域或整个屏幕的显示设备上下文环境的句柄，以后可以在GDI函数中使用该句柄来在设备上下文环境中绘图
        /// </summary>
        /// <param name="hWnd">备上下文环境被检索的窗口的句柄，如果该值为NULL，GetDC则检索整个屏幕的设备上下文环境</param>
        /// <returns>如果成功，返回指定窗口客户区的设备上下文环境；如果失败，返回值为Null</returns>
        [DllImport("user32.dll")]
        public static extern IntPtr GetDC(IntPtr hWnd);

        /// <summary>
        ///【Win32 API】  函数释放设备上下文环境（DC）供其他应用程序使用。函数的效果与设备上下文环境类型有关。它只释放公用的和设备上下文环境，对于类或私有的则无效
        /// </summary>
        /// <param name="hWnd">指向要释放的设备上下文环境所在的窗口的句柄</param>
        /// <param name="hdc">指向要释放的设备上下文环境的句柄</param>
        /// 指向要释放的设备上下文环境的句柄
        /// <returns>返回值说明了设备上下文环境是否释放；如果释放成功，则返回值为1；如果没有释放成功，则返回值为0</returns>
        [DllImport("user32.dll")]
        public static extern int ReleaseDC(IntPtr hWnd, IntPtr hdc);

        [DllImport("user32.dll")]
        public static extern int ReleaseDC(IntPtr hdc);
        /// <summary>
        /// 【Win32 API】 该函数检索指定坐标点的像素的RGB颜色值
        /// </summary>
        /// <param name="hdc">设备环境句柄</param>
        /// <param name="x">指定要检查的像素点的逻辑X轴坐标</param>
        /// <param name="y">指定要检查的像素点的逻辑Y轴坐标</param>
        /// <returns>返回值是该象像点的RGB值。如果指定的像素点在当前剪辑区之外；那么返回值是CLR_INVALID</returns>
        [DllImport("gdi32.dll")]
        public static extern int GetPixel(IntPtr hdc, int nXPos, int nYPos);

        /// <summary>
        ///【Win32 API】 对指定的窗口句柄进行截图
        /// </summary>
        /// <param name="hwnd">窗口句柄</param>
        /// <param name="hdcBlt">设备环境句柄的bitmap对象</param>
        /// <param name="nFlags">Optional flags,Specifies the drawing options. It can be one of the following values</param>
        /// <returns></returns>
        [DllImport("user32.dll")]
        public static extern bool PrintWindow(IntPtr hwnd, IntPtr hdcBlt, UInt32 nFlags);



        /// <summary>
        ///  根据句柄进行对窗口截图，窗口可以被遮盖,但不支持窗口最小化
        /// </summary>
        /// <param name="hWnd">截图的窗口句柄</param>
        /// <param name="pointSrc">开始坐标点</param>
        /// <param name="pointDest">结束坐标点</param>
        /// <param name="isFull">是否全屏截图,当全屏时pointSrc和pointDest作用失效</param>
        /// <returns></returns>
        public static System.Drawing.Bitmap GetBitmap(IntPtr hWnd, Point pointSrc, Point pointDest, bool isFull)
        {
            System.Drawing.Bitmap bitmap = null;
            System.Drawing.Rectangle rectangle;
            Window.GetClientRect(hWnd, out  rectangle);    //获取窗口大小
            //创建DC 一个是原图,一个是区域裁剪DC
            IntPtr hdcScreen = GetDC(IntPtr.Zero);         //获取一个 设备上下文环境DC
            //--------------------------------------------------------------------------------------------------------------  
            //创建原始图标 DC 
            IntPtr hdcSrc = CreateCompatibleDC(hdcScreen);
            IntPtr bitmapSrc = CreateCompatibleBitmap(hdcScreen, (rectangle.Right - rectangle.Left), (rectangle.Bottom - rectangle.Top));
            //--------------------------------------------------------------------------------------------------------------  
            //创建目标图标 DC
            IntPtr hdcDest = CreateCompatibleDC(hdcScreen);
            //对目标图片长度高度进行检测  
            int nWidth; int nHeight; int nXSrc; int nYSrc;
            if (isFull)
            {
                nXSrc = 0;
                nYSrc = 0;
                nWidth = (rectangle.Right - rectangle.Left);
                nHeight = (rectangle.Bottom - rectangle.Top);
            }
            else
            {
                nXSrc = pointSrc.X < 0 ? 0 : pointSrc.X;
                nYSrc = pointSrc.Y < 0 ? 0 : pointSrc.Y;
                nWidth = (pointDest.X - nXSrc) > (rectangle.Right - rectangle.Left) ? (rectangle.Right - rectangle.Left) : (pointDest.X - nXSrc);
                nHeight = (pointDest.Y - nYSrc) > (rectangle.Bottom - rectangle.Top) ? (rectangle.Bottom - rectangle.Top) : (pointDest.Y - nYSrc);
            }
            IntPtr bitmapDest = CreateCompatibleBitmap(hdcScreen, nWidth, nHeight);
            //-------------------------------------------------------------------------------------------------------------- 
            if (bitmapSrc != IntPtr.Zero && bitmapDest != IntPtr.Zero)
            {
                SelectObject(hdcSrc, bitmapSrc);
                PrintWindow(hWnd, hdcSrc, 0x01);
                SelectObject(hdcDest, bitmapDest);
                BitBlt(hdcDest, 0, 0, nWidth, nHeight, hdcSrc, nXSrc, nYSrc, (UInt32)0xcc0020);
                if (bitmapDest != IntPtr.Zero)
                {
                    bitmap = System.Drawing.Bitmap.FromHbitmap(bitmapDest);
                }
            }
            //-------------------------------------------------------------------------------------------------------------- 
            ReleaseDC(IntPtr.Zero, hdcScreen);
            DeleteObject(bitmapSrc);
            DeleteDC(hdcSrc);
            DeleteObject(bitmapDest);
            DeleteDC(hdcDest);
            return bitmap;
        }

        /// <summary>
        ///  获取 窗口中指定坐标像素
        /// </summary>
        /// <param name="hWnd">窗口句柄</param>
        /// <param name="X">X坐标</param>
        /// <param name="Y">Y坐标</param>
        /// <returns></returns>
        public static System.Drawing.Color GetPixelEx(IntPtr hWnd, int X, int Y)
        {
            Point pointSrc = new Point(X, Y);
            Point pointDest = new Point(pointSrc.X + 1, pointSrc.Y + 1);

            System.Drawing.Bitmap bitmap = GetBitmap(hWnd, pointSrc, pointDest, false);
            if (bitmap != null && bitmap.Width > 0 && bitmap.Height > 0)
            {
                return bitmap.GetPixel(0, 0);
            }
            else
            {
                return new System.Drawing.Color();
            }
        }


        ///  <summary>   
        /// 判断图形里是否存在另外一个图形 并返回所在位置   
        /// </summary>   
        /// <param name="p_SourceBitmap">原始图形</param>   
        /// <param name="p_PartBitmap">小图形</param>   
        /// <param name="p_Float">溶差</param>   
        /// <returns>坐标</returns>   
        public static Point GetImageContains(System.Drawing.Bitmap p_SourceBitmap, System.Drawing.Bitmap p_PartBitmap, int p_Float)
        {
            int _SourceWidth = p_SourceBitmap.Width;
            int _SourceHeight = p_SourceBitmap.Height;

            int _PartWidth = p_PartBitmap.Width;
            int _PartHeight = p_PartBitmap.Height;


            BitmapData _SourceData = p_SourceBitmap.LockBits(new Rectangle(0, 0, _SourceWidth, _SourceHeight), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
            IntPtr p_SourcePtr = _SourceData.Scan0;

            byte[] _SourceByte = new byte[_SourceData.Stride * _SourceHeight];
            System.Runtime.InteropServices.Marshal.Copy(p_SourcePtr, _SourceByte, 0, _SourceByte.Length);
            p_SourceBitmap.UnlockBits(_SourceData);


            BitmapData _PartData = p_PartBitmap.LockBits(new Rectangle(0, 0, _PartWidth, _PartHeight), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
            IntPtr p_PartPtr = _PartData.Scan0;
            byte[] _PartByte = new byte[_PartData.Stride * _PartHeight];
            System.Runtime.InteropServices.Marshal.Copy(p_PartPtr, _PartByte, 0, _PartByte.Length);   //复制出p_PartBitmap的相素信息   
            p_PartBitmap.UnlockBits(_PartData);

            for (int i = 0; i != _SourceHeight; i++)
            {
                if (_SourceHeight - i < _PartHeight) return new System.Drawing.Point(-1, -1);  //如果 剩余的高 比需要比较的高 还要小 就直接返回                
                int _PointX = -1;    //临时存放坐标 需要包正找到的是在一个X点上   
                bool _SacnOver = true;   //是否都比配的上   
                for (int z = 0; z != _PartHeight - 1; z++)       //循环目标进行比较   
                {
                    int _TrueX = GetImageContains(_SourceByte, _PartByte, i * _SourceData.Stride, _SourceWidth, _PartWidth, p_Float);

                    if (_TrueX == -1)   //如果没找到    
                    {
                        _PointX = -1;    //设置坐标为没找到   
                        _SacnOver = false;   //设置不进行返回   
                        break;
                    }
                    else
                    {
                        if (z == 0) _PointX = _TrueX;
                        if (_PointX != _TrueX)   //如果找到了 也的保证坐标和上一行的坐标一样 否则也返回   
                        {
                            _PointX = -1;//设置坐标为没找到   
                            _SacnOver = false;  //设置不进行返回   
                            break;
                        }
                    }
                }
                if (_SacnOver) return new System.Drawing.Point(_PointX, i);
            }

            return new System.Drawing.Point(-1, -1);
        }


        /// <summary>   
        /// 判断图形里是否存在另外一个图形 所在行的索引   
        /// </summary>   
        /// <param name="p_Source">原始图形数据</param>   
        /// <param name="p_Part">小图形数据</param>   
        /// <param name="p_SourceIndex">开始位置</param>   
        /// <param name="p_SourceWidth">原始图形宽</param>   
        /// <param name="p_PartWidth">小图宽</param>   
        /// <param name="p_Float">溶差</param>   
        /// <returns>所在行的索引 如果找不到返回-1</returns>   
        private static int GetImageContains(byte[] p_Source, byte[] p_Part, int p_SourceIndex, int p_SourceWidth, int p_PartWidth, int p_Float)
        {
            int _PartIndex = 0;
            int _SourceIndex = p_SourceIndex;
            for (int i = 0; i < p_SourceWidth; i++)
            {
                if (p_SourceWidth - i < p_PartWidth) return -1;
                Color _CurrentlyColor = Color.FromArgb((int)p_Source[_SourceIndex + 3], (int)p_Source[_SourceIndex + 2], (int)p_Source[_SourceIndex + 1], (int)p_Source[_SourceIndex]);
                Color _CompareColoe = Color.FromArgb((int)p_Part[3], (int)p_Part[2], (int)p_Part[1], (int)p_Part[0]);
                _SourceIndex += 4;

                bool _ScanColor = ScanColor(_CurrentlyColor, _CompareColoe, p_Float);

                if (_ScanColor)
                {
                    _PartIndex += 4;
                    int _SourceRVA = _SourceIndex;
                    bool _Equals = true;
                    for (int z = 0; z != p_PartWidth - 1; z++)
                    {
                        _CurrentlyColor = Color.FromArgb((int)p_Source[_SourceRVA + 3], (int)p_Source[_SourceRVA + 2], (int)p_Source[_SourceRVA + 1], (int)p_Source[_SourceRVA]);
                        _CompareColoe = Color.FromArgb((int)p_Part[_PartIndex + 3], (int)p_Part[_PartIndex + 2], (int)p_Part[_PartIndex + 1], (int)p_Part[_PartIndex]);

                        if (!ScanColor(_CurrentlyColor, _CompareColoe, p_Float))
                        {
                            _PartIndex = 0;
                            _Equals = false;
                            break;
                        }
                        _PartIndex += 4;
                        _SourceRVA += 4;
                    }
                    if (_Equals) return i;
                }
                else
                {
                    _PartIndex = 0;
                }
            }
            return -1;
        }

        /// <summary>   
        /// 检查色彩(可以根据这个更改比较方式   
        /// </summary>   
        /// <param name="p_CurrentlyColor">当前色彩</param>   
        /// <param name="p_CompareColor">比较色彩</param>   
        /// <param name="p_Float">溶差</param>   
        /// <returns></returns>   
        private static bool ScanColor(Color p_CurrentlyColor, Color p_CompareColor, int p_Float)
        {
            int _R = p_CurrentlyColor.R;
            int _G = p_CurrentlyColor.G;
            int _B = p_CurrentlyColor.B;
            return (_R <= p_CompareColor.R + p_Float && _R >= p_CompareColor.R - p_Float) && (_G <= p_CompareColor.G + p_Float && _G >= p_CompareColor.G - p_Float) && (_B <= p_CompareColor.B + p_Float && _B >= p_CompareColor.B - p_Float);
        }

        /// <summary>
        ///  根据窗口句柄 全屏 检测该窗口是否有跟比较的位图一样的
        /// </summary>
        /// <param name="hWnd">窗口句柄</param>
        /// <param name="bitmap">比较参照的位图</param>
        /// <param name="p_Float">溶差</param>
        /// <returns></returns>
        public static System.Drawing.Point HaveBitmap(IntPtr hWnd, System.Drawing.Bitmap bitmap, int p_Float)
        {
            System.Drawing.Bitmap dcDitmap = GetBitmap(hWnd, new System.Drawing.Point(0, 0), new System.Drawing.Point(0, 0), true);
            if (dcDitmap == null)
            {
                return new System.Drawing.Point(-1, -1);
            }
            else
            {
                return RuoTeng.Win32.Image.GetImageContains(dcDitmap, bitmap, p_Float);
            }
        }

        /// <summary>
        /// 根据窗口句柄 和指定的区域截图后再 检测该窗口是否有跟比较的位图一样的
        /// </summary>
        /// <param name="hWnd">窗口句柄</param>
        /// <param name="pointSrc">开始点</param>
        /// <param name="pointDest">结束点</param>
        /// <param name="bitmap">参照的位图</param>
        /// <param name="p_Float">溶差</param>
        /// <returns></returns>
        public static System.Drawing.Point HaveBitmap(IntPtr hWnd, System.Drawing.Point pointSrc, System.Drawing.Point pointDest, System.Drawing.Bitmap bitmap, int p_Float)
        {
            if (bitmap.Width > (pointDest.X - pointSrc.X))
            {
                int w = bitmap.Width - (pointDest.X - pointSrc.X) / 2;
                int mod = bitmap.Width - (pointDest.X - pointSrc.X) % 2;
                pointSrc.X = mod > 0 ? w + 1 : w;
                pointDest.X = mod > 0 ? w + 1 : w;
            }
            if (bitmap.Width > (pointDest.Y - pointSrc.Y))
            {
                int h = bitmap.Width - (pointDest.Y - pointSrc.Y) / 2;
                int mod = bitmap.Width - (pointDest.Y - pointSrc.Y) % 2;
                pointSrc.Y = mod > 0 ? h + 1 : h;
                pointDest.Y = mod > 0 ? h + 1 : h;
            }
            System.Drawing.Bitmap dcDitmap = GetBitmap(hWnd, pointSrc, pointDest, false);
            if (dcDitmap == null)
            {
                return new System.Drawing.Point(-1, -1);
            }
            else
            {
                return GetImageContains(dcDitmap, bitmap, p_Float);
            }
        }

        /// <summary>
        /// 根据窗口句柄 和指定的区域截图后再 检测该窗口是否有跟比较的位图一样的
        /// </summary>
        /// <param name="hWnd">窗口句柄</param>
        /// <param name="pointSrc">指定的矩形</param>
        /// <param name="bitmap">参照的位图</param>
        /// <param name="p_Float">溶差</param>
        /// <returns></returns>
        public static System.Drawing.Point MinHaveBitmap(IntPtr hWnd, System.Drawing.Rectangle rect, System.Drawing.Bitmap bitmap, int p_Float)
        {
            System.Drawing.Point pointSrc = new System.Drawing.Point(rect.Left, rect.Top);
            System.Drawing.Point pointDest = new System.Drawing.Point(rect.Right, rect.Bottom);
            return HaveBitmap(hWnd, pointSrc, pointDest, bitmap, p_Float);
        }
    }
}


